﻿#pragma once

#include "CoreMinimal.h"
#include "SoftRendererShader.generated.h"

struct FRenderObjectVertex;

/**
 * Shader内置变量信息
 */
struct FShaderBuiltinVariables
{
	/**
	 * 当前的模型矩阵，用于将顶点/方向矢量从模型空间变换到世界空间
	 */
	FMatrix Matrix_ObjectToWorld;

	/**
	 * Object2World的逆矩阵，用于将顶点/方向矢量从世界空间变换到模型空间
	 */
	FMatrix Matrix_WorldToObject;

	/**
	 * 当前的模型观察投影矩阵，用于将顶点/方向矢量从模型空间变换到裁剪空间
	 */
	FMatrix Matrix_MVP;

	/**
	 * 当前的模型观察矩阵，用于将顶点/方向矢量从模型空间变换到观察空间
	 */
	FMatrix Matrix_MV;

	/**
	 * 当前的观察矩阵，用于将顶点/方向矢量从世界空间变换到观察空间
	 */
	FMatrix Matrix_V;

	/**
	 *  当前的投影矩阵，用于将顶点/方向矢量从观察空间变换到裁剪空间
	 */
	FMatrix Matrix_P;

	/**
	 * Matrix_MV的装置矩阵
	 */
	FMatrix T_MV;

	/**
	 * Matrix_MV的逆转置矩阵，用于将法线从模型空间变换到观察空间，也可用于得到Matrix_MV的逆矩阵
	 */
	FMatrix IT_MV;
	
};

/**
 * 顶点着色器输出
 * 同时也作为像素着色器的输入
 */
struct FVertexToPixelInterpolants
{
	friend class USoftRenderer;
	friend class FRasterizationCPU;
	friend class FRasterizationGPU;
	
public:
	/**
	 * 齐次裁剪空间的位置
	 */
	FVector4 Position;

	/**
	 * 顶点色
	 */
	FLinearColor Color;

	/**
	 * UV数组
	 */
	TArray<FVector2D> UVs;

	/**
	 * 顶点法线
	 */
	FVector Normal;

	/**
	 * 顶点切线
	 */
	FVector Tangent;
	

protected:
	/**
	 * 浮点数屏幕坐标
	 */
	FVector2D ScreenPos;

	/**
	 * 1 / Position.W
	 */
	float RHW;
	
public:
	FVertexToPixelInterpolants()
	{
		RHW = 1;
	}

	/**
	 * 透视除法用于GPU光栅化
	 */
	void PerspectiveDivisionGPU(FIntPoint ViewportSize)
	{
		RHW = 1 / Position.W;

		// 透视除法, 齐次坐标空间 /w 归一化到NDC坐标系中 
		Position *= RHW;

		// 计算浮点数屏幕坐标
		ScreenPos.X = (Position.X + 1.0f) * ViewportSize.X * 0.5f;
		ScreenPos.Y = (1.0f - Position.Y) * ViewportSize.Y * 0.5f;
	}

	/**
	 * 透视除法用于CPU光栅化
	 */
	void PerspectiveDivisionCPU(FIntPoint ViewportSize)
	{
		RHW = 1 / Position.W;

		// 透视除法, 齐次坐标空间 /w 归一化到NDC坐标系中 
		Position *= RHW;

		// 计算浮点数屏幕坐标
		ScreenPos.X = (Position.X + 1.0f) * ViewportSize.X * 0.5f;
		ScreenPos.Y = (1.0f - Position.Y) * ViewportSize.Y * 0.5f;

		// CPU光栅化, 颜色, UV等也需要乘以RHW用于后续的光栅化操作
		Color *= RHW;
	}
	
	/**
	 * 齐次裁剪空间做裁剪和使用扫描线光栅化时候获取两个顶点的光栅化插值顶点信息
	 */
	static FVertexToPixelInterpolants LerpVertex(const FVertexToPixelInterpolants& Point1, const FVertexToPixelInterpolants& Point2, float Weight)
	{
		FVertexToPixelInterpolants NewVertex;
		NewVertex.Position = FMath::Lerp(Point1.Position, Point2.Position, Weight);
		NewVertex.ScreenPos = FMath::Lerp(Point1.ScreenPos, Point2.ScreenPos, Weight);
		NewVertex.Color = FMath::Lerp(Point1.Color, Point2.Color, Weight);
		NewVertex.RHW = FMath::Lerp(Point1.RHW, Point2.RHW, Weight);

		NewVertex.UVs.SetNumZeroed(Point1.UVs.Num());
		for (int32 Index = 0, Count = Point1.UVs.Num(); Index < Count; ++Index)
		{
			NewVertex.UVs[Index] = FMath::Lerp(Point1.UVs[Index], Point2.UVs[Index], Weight);
		}
		
		NewVertex.Normal = FMath::Lerp(Point1.Normal, Point2.Normal, Weight);
		NewVertex.Tangent = FMath::Lerp(Point1.Tangent, Point2.Tangent, Weight);

		return NewVertex;
	}

	/**
	 * 使用扫描线光栅化时候使用，获取两个顶点的插值步进信息，根据像素宽度
	 */
	static FVertexToPixelInterpolants GetStepVertex(const FVertexToPixelInterpolants& Point1, const FVertexToPixelInterpolants& Point2, float Width)
	{
		FVertexToPixelInterpolants NewVertex;

		const float Inv = 1.0f / Width;
		NewVertex.ScreenPos = (Point2.ScreenPos - Point1.ScreenPos) * Inv;
		NewVertex.Color = (Point2.Color - Point1.Color) * Inv;
		NewVertex.RHW = (Point2.RHW - Point1.RHW) * Inv;

		return NewVertex;
	}

	/**
	 * 使用扫描线光栅化时候使用，步进插值当前顶点的信息
	 */
	static void StepVertex(FVertexToPixelInterpolants& Vertex, const FVertexToPixelInterpolants& Step)
	{
		Vertex.Position += Step.Position;
		Vertex.RHW += Step.RHW;
		Vertex.Color += Step.Color;
	}

	/**
	 * 基于重心坐标插值
	 */
	static FVertexToPixelInterpolants InterpVertex(const FVertexToPixelInterpolants& Point1, const FVertexToPixelInterpolants& Point2,
		const FVertexToPixelInterpolants& Point3, float C1, float C2, float C3, float CurRHW)
	{
		FVertexToPixelInterpolants NewVertex;

		NewVertex.Position = Point1.Position * C1 + Point2.Position * C2 + Point3.Position * C3;
		NewVertex.ScreenPos = Point1.ScreenPos * C1 + Point2.ScreenPos * C2 + Point3.ScreenPos * C3;
		NewVertex.Color = Point1.Color * C1 + Point2.Color * C2 + Point3.Color * C3;
		
		NewVertex.UVs.SetNum(Point1.UVs.Num());
		for (int32 Index = 0, Count = Point1.UVs.Num(); Index < Count; ++Index)
		{
			NewVertex.UVs[Index] = Point1.UVs[Index] * C1 + Point2.UVs[Index] * C2 + Point3.UVs[Index] * C3;
		}
		
		NewVertex.Normal = Point1.Normal * C1 + Point2.Normal * C2 + Point3.Normal * C3;
		NewVertex.Tangent = Point1.Tangent * C1 + Point2.Tangent * C2 + Point3.Tangent * C3;
		NewVertex.RHW = CurRHW;

		return NewVertex;
	}
};

/**
 * 抽象着色器对象
 */
UCLASS(Abstract, Blueprintable, BlueprintType)
class SOFTRENDERER_API USoftRendererShader : public UObject
{
	GENERATED_UCLASS_BODY()

public:
	FShaderBuiltinVariables* ShaderBuiltinVariables;

	/** 开启AlphaTest的Shader中需要在构造函数中设置bDiscardPixel = true */
	uint8 bDiscardPixel : 1;
	
public:
	virtual FVertexToPixelInterpolants RunVertexShader(const FRenderObjectVertex& Vertex) { return FVertexToPixelInterpolants(); }

	virtual FLinearColor RunPixelShader(const FVertexToPixelInterpolants& Pixel, bool& bDiscardCurPixel) { return FLinearColor::White; }
	
};
